package FlHandle;
use strict;
use warnings;

use CON;                                                      
use Cwd                     qw{abs_path       cwd};           
use DDP;                                                      
use Digest::SHA             qw/sha512_hex/;                   
use Env                     qw(USER);                         
use File::Basename;                                           
use File::Path              qw(make_path);                    
use File::Slurp;                                              
use IO::File;                                                 
use IO::Uncompress::Bunzip2 qw(bunzip2        $Bunzip2Error); 
use IO::Uncompress::Gunzip  qw(gunzip         $GunzipError);  
use List::MoreUtils         qw/each_arrayref /;                 
use Math::Random::ISAAC;                                      
use Text::ASCIITable;                                         
use Text::Aligner           qw/align/;                        
use Time::HiRes             qw/gettimeofday/;                 

$| = 1;

use Exporter;
use vars qw(@ISA @EXPORT);
use Carp::Always::Color;
push @ISA,    qw(Exporter);
push @EXPORT, qw(
  AoA2AsciiTable
  Hashref2AsciiTable
  FlHandle
  GetRndHex
  GetRndStr
  GetTmpDir
  GetYWRndStr
  GetYWStr
  ReadPartOfZFile
  SortBits
);


sub ReadPartOfZFile 
{
    my ( $fi, $rstr_back, $length ) = @_;
    my $zf;
    &FlHandle( $fi, \$zf );
    if ($length) { $zf->read( $rstr_back, $length ); }
    else         { $zf->read($rstr_back); }
    close $zf;
}

sub FlHandle
{    #_{{{
    my ( $fi, $rfile, $rMainWarningStr ) = @_;
    if ( not $fi ) {
        Carp::cluck "-W- no file provided";
        if ($rMainWarningStr) {
            $$rMainWarningStr .= sprintf "%s no file provided;\n", &color_or_not( q/-W-/, q/red/ );
        }
        return;
    }
    my $absF;
    if ( $fi and -e $fi ) {
        $absF = abs_path($fi);
    } else {
        Carp::cluck "-W- cannot see $fi";
        if ($rMainWarningStr) {
            $$rMainWarningStr .= sprintf "%s cannot see %s ;\n", &color_or_not( q/-W-/, q/red/ ), &color_or_not( $fi, q/red/ );
        }
        return;
    }

    my ( $tfbase, $tDir, $suffix ) = fileparse( $absF, qr{\.[^.]*(\.(?:gz|bz2))?} );

#    printf "-I- reading %8s\n", &color_or_not($absF);

    if    ( $suffix =~ /\.gz/ )  { $$rfile = IO::Uncompress::Gunzip->new($fi)  or Carp::confess "-E-cannot open $fi; $GunzipError"; }
    elsif ( $suffix =~ /\.bz2/ ) { $$rfile = IO::Uncompress::Bunzip2->new($fi) or Carp::confess "-E-cannot open $fi; $Bunzip2Error"; }
    else                         { $$rfile = IO::File->new($fi)                or Carp::confess "-E-cannot open $fi"; }

    return;

}    #_}}}

sub GetTmpDir
{#_{{{
    my ($reuse) = @_;
    my $opt_dir = qq{/tmp/$USER};
    if (not -d $opt_dir){
            make_path($opt_dir) or Carp::confess "-E-cannot create $opt_dir";
    }


    my $yywwString =  &GetYWStr;
    my $Dir = qq#$opt_dir/$yywwString#;
    my $tDir;
    my @Ar2 = ('a' .. 'z');
    my @Ar3 = map { $_ = ".".$_; } ( 1 .. 100);
    foreach my $lt ( @Ar2, @Ar3 ) {
        $tDir = qq#$Dir$lt#;
        if ( $reuse and -d $tDir ) {
            return $tDir;
        } elsif (  -e $tDir ) {
            next;
        } else  {
            make_path($tDir) or Carp::confess "-E-cannot make path $tDir";
            return $tDir;
        }
    }

}#_}}}

sub GetYWStr
{
    my ($two_four_digit_year) = @_;
    my $yywwString;
    if ( $two_four_digit_year and ( $two_four_digit_year == 4 ) ) {
        
        $yywwString = qx(date +"%Yw%Vd%u%H");
    } else {
#        $yywwString = qx(date +"%yww%Wd%w%H");
        $yywwString = qx(date +"%yw%Vd%u%H");
    }
    chomp $yywwString;
    return $yywwString;
}


sub GetRndStr
{#_{{{
    my ($Digit) = @_;
#    $Digit //=  10;                            # the defined or assign // operator not working? 20ww23
    if (defined $Digit){ } else { $Digit = 10; }
    my $rn0    = q//;
    my $oDigit = $Digit;
    while ( $Digit > 0 ) {
        $rn0 .= sprintf "%010d", Math::Random::ISAAC->new((gettimeofday)[-1])->irand();
        $Digit -= 10;
    }
    my $rngstr = substr( $rn0, 0, $oDigit );
    return $rngstr;
}#_}}}

sub GetRndHex
{#_{{_
    my ($Digit) = @_;
    if (defined $Digit){ } else { $Digit = 8; }
    my $rndN = &GetRndStr($Digit);
    my $rndH = sha512_hex($rndN);
    my $rngstr = substr( $rndH, 0, $Digit );
    
    return $rngstr;
}#_}}_

sub GetYWRndStr
{
    my ($Digit) = @_;
    my $s1= &GetYWStr(10);
#    my $s2 = sprintf "%.8x", Math::Random::ISAAC->new((gettimeofday)[-1])->irand();
    my $s2 = &GetRndHex($Digit);
    return qq/${s1}_$s2/;
}

sub SortBits
{
    my ($rA) = @_;
    my @A2 = sort { 
        ( $a =~ s/\[.*//rg ) cmp( $b =~ s/\[.*//rg ) 
            or 
        ( $a =~ s/\[\d+\]//rg ) cmp( $b =~ s/\[\d+\]//rg ) 
            or 
        ( $a =~ /\[(\d+)\]/ )[0] <=> ( $b =~ /\[(\d+)\]/ )[0] 
            or 
        ( $a =~ /\[(\d+)\]/g )[1] <=> ( $b =~ /\[(\d+)\]/g )[1] 
            or 
        ( $a =~ /\[(\d+)\]/g )[2] <=> ( $b =~ /\[(\d+)\]/g )[2] } @$rA;

    @$rA = @A2;
}

sub AoA2AsciiTable 
{    #_{{_
    my ( $rA, $rstr ) = @_;
    my $rrA = ref $rA;
    if ( $rrA and $rrA eq q/ARRAY/ ) {
        my $rAh  = shift @$rA;
        my $rrAh = ref $rAh;
        if ( $rrAh and $rrAh eq q/ARRAY/ ) {
            my $tb = Text::ASCIITable->new();
            $tb->setCols($rAh);
            foreach my $ci (@$rAh) { $tb->alignCol( $ci, q/right/ ); }
            my $ea   = &each_arrayref(@$rA);
            my @A184 = ();
            while ( my @A185 = $ea->() ) { my $st = align( q/auto/, @A185 ); push @A184, $st; }
            $tb->addRow( \@A184 );
            if ( defined $rstr ) {
                $$rstr = $tb->draw();
            } else {
                return $tb->draw();
            }    # end of if-else on condition defined $rstr

        } else {
            Carp::confess "-e-please provide ArrayRef to ArrayRef";
        }

    } else {
        Carp::confess "-e-please provide ArrayRef to ArrayRef";
    }    # end of if-else on condition ref $rA eq q/ARRAY/
}    # end_of func AoA2AsciiTable}}

sub Hashref2AsciiTable 
{#_{{_
    my ( $rH, $rstr, $str_key, $str_value ) = @_;
    my $rrH = ref $rH;
    if ( $rrH and $rrH eq q/HASH/ ) {
        my $tb  = Text::ASCIITable->new();
        my @Ahd = ();
        if ( defined $str_key and defined $str_value ) {
            @Ahd = ( $str_key, $str_value );
        } else {
            @Ahd = qw/key value/;
        }    # end of if-else on condition defined $str_key and defined $str_value
        $tb->setCols( \@Ahd );
        foreach my $ci (@Ahd) { $tb->alignCol( $ci, q/right/ ); }
        my @Ak  = sort keys %$rH;
        my $stk = align( q/auto/, @Ak );
        my @Av  = map { $rH->{$_} } @Ak;
        my $stv = align( q/auto/, @Av );
        $tb->addRow( [ $stk, $stv ] );

        if ( defined $rstr ) {
            my $r_rstr = ref $rstr;
            if ( $r_rstr and $r_rstr eq q/SCALAR/ ) {
                $$rstr = $tb->draw();
            } else {
                Carp::cluck "-w- 2nd argument should be reference to scalar, like \\\$string";
                printf "-i- returning table string, please do %8s\n", &CONm( "my \$st= \&Hashref2AsciiTable()", 4 );
                return $tb->draw();
            }    # end of if-else on condition $r_rstr and $r_rstr eq q/SCALAR/
        } else {
            return $tb->draw();
        }
    } else {
        Carp::confess "-e-please provide HashRef";
    }    # end of if-else on condition ref $rA eq q/ARRAY/
}#_}}_

1;
__END__

=head1 NAME

=head2  FlHandle() 

Assign File handle onto the passed-in reference to scalar
-- regardless file is gzip'ed, bzip2'ed or nonzip'ed at all


    my $zctf;
    &FlHandle($fi, \$zctf);
    # and we can use File::Slurp's read_file, like
    my $str = read_file($zctf);

=head2  ReadPartOfZFile() 

To read the first part of a gzipped/bzipped/non-zipped
file. E.g., to read in A.txt.gz file's first
20k content, into scalar string $str1.

    my $str1;
    my $in_file = qq(A.txt.gz);
    &FlHandle::ReadPartOfZFile( $in_file, \$str1, 20_480);



=head2  GetTmpDir()

create a temporary directory in /tmp/$USER/ area

    my $tdir = &GetTmpDir(0);
    my $outF = qq($tdir/$rngstr.$bct.$fbname.txt);


 
=head2  GetRndStr(N) 

returns an N-digit long random number (default N=10).
This random number is encrypto-wise secure from ISAAC.
 
=head3 See Also

Math::Random::ISAAC
 
=head2  GetRndHex(N)

returns an N-character long random hex string ([0-9a-f]
character set, and N=8 by default). This hex string
is (1) using a 10-digit random number from &GetRndStr
as a seed, and (2) acquiring the seed's SHA-512 hex
representation through &Digest::SHA::sha512_hex();
 
=head3 See Also

Math::Random::ISAAC

Digest::SHA
 
=head2  GetYWStr() 

returns a string in 19w41d312 format. 19w41d312
stands for year 2019, workweek 41, day-3, hour-12.
GetYWStr(4) would return the 4-digit year as 2019

=head2  GetYWRndStr()

returns the yyww-string appended with random number above.


